package com.jzoom.zoom.pay;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.citywithincity.db.Db;
import com.citywithincity.utils.DateTimeUtil;
import com.citywithincity.utils.Sequence;
import com.jzoom.zoom.pay.models.CreatePay;
import com.jzoom.zoom.pay.models.CreateRefund;
import com.jzoom.zoom.pay.models.Pay;
import com.jzoom.zoom.pay.models.Refund;

/**
 * 
 * 对账逻辑：
 * 
 * 1、支付网关=》支付平台对账
 * 	结果：
 *   本地支付订单+平台支付订单
 *   本地退款订单+平台退款订单
 *   
 *   
 * 
 * 2、业务对账：
 *  业务订单 + 对账清算订单  ： 业务订单为应付，清算订单为实付
 *  业务退款 + 对账清算退款  ： 业务订单为应退款，清算退款为实退款
 * 
 *  有点：
 *  1、支付网关只负责与支付平台通信
 *  2、支付网关单独对账
 *  3、统一的对接接口
 *  
 *  
 *  
 *  
 *  
 *  支付：业务支付流程
 *  
 *  1、业务新增订单，申请支付网关预支付
 *  2、用户完成支付，通知业务
 *  3、业务进行后续处理
 *  
 *  
 *  退款流程：
 *  1、申请支付网关退款
 *  2、完成退款，业务修改状态
 *  
 *  
 *  支付网关给出对接sdk，服务器对接
 *  
 *  
 *  
 * 
 *   
 * 
 *  
 * 
 * 
 * 
 * 
 * 
 * 
 * @author jzoom
 *
 */
public class PayManager implements PayAdapterListener {

	private static PayManager manager = new PayManager();
	
	private Map<String, PayAdapter> adapterMap;
	
	
	public PayManager(){
		adapterMap = new ConcurrentHashMap<String, PayAdapter>();
	}
	
	/**
	 * 通知url
	 */
	private String payNotifyUrl;
	
	/**
	 * 单例
	 * @return
	 */
	public static PayManager me(){
		return manager;
	}
	
	
	/**
	 * 根据注册时候的key，获取adapter
	 * @param key
	 * @return
	 * @throws PayException 
	 */
	public PayAdapter getPayAdapter(String mchId,String channel) throws PayException{
		PayAdapter data = adapterMap.get(getKey(mchId, channel));
		if(data == null) {
			throw new PayException(PayException.NOT_SUPPORTED_CHANNEL, "商户不支持本支付方式");
		}
		return data;
	}
	
	/**
	 * 注册
	 * @param key
	 * @param adapter
	 */
	public void registerPayAdapter(String mchId,String channel,PayAdapter adapter){
		adapter.setNotifyUrl(getPayNotifyUrl(getKey(mchId, channel)));
		adapter.setPayAdapterListener(this);
		adapterMap.put(getKey(mchId, channel), adapter);
	}

	
	public String getKey(String mchId,String channel) {
		return new StringBuilder(channel).append(mchId).toString();
	}
	
	/**
	 * 获取通知
	 * @param key
	 * @return
	 */
	public String getPayNotifyUrl(String key) {
		if(payNotifyUrl==null) {
			throw new RuntimeException("请先设置notifyUrl");
		}
		return new StringBuilder(payNotifyUrl).append( key ).toString();
	}

	/**
	 * 设置外部推送链接
	 * @param payNotifyUrl
	 */
	public void setPayNotifyUrl(String payNotifyUrl) {
		this.payNotifyUrl = payNotifyUrl;
	}


	/**
	 * 成功之后需要通知到客户端
	 */
	@Override
	public void onPaySuccess(NotifyPayInfo info) {
		//TaskQueue.getInstance().execute("", "");
		
		//查询这个app的配置,配置常驻内存中
		
		
		//查询到了以后，根据配置，推送到app的服务器
		
		
		
		
	}

	
	
	public Pay create(CreatePay pay) throws PayException {
		//先保存一下，然后去下单
		Pay data = new Pay();
		data.setId(Sequence.getUniqueId());
		data.setCreateTime( System.currentTimeMillis()/ 1000 );
		
		data.setMchId(pay.getMchId());
		data.setOrderNo(pay.getOrderNo());
		data.setTitle(pay.getTitle());
		data.setBody(pay.getBody());
		data.setAmount(pay.getAmount());
		data.setClientIp(pay.getClientIp());
		data.setChannel(pay.getChannel());
		
		data.setDate(  DateTimeUtil.shortDate()  );
		
		
		Db.ar(Pay.class).insert(data);
		try {
			getPayAdapter(pay.getMchId(), pay.getChannel()).create(data);
			Db.ar(Pay.class).update(data,"^credential$");
		}catch (PayException e) {
			//保存状态
			data.setErrCode(e.getCode());
			data.setErrMsg(e.getError());
			Db.ar(Pay.class).update(data,"^errCode|errMsg$");
		} catch (IOException e) {
			data.setErrCode(PayException.IO_ERROR);
			data.setErrMsg("网络错误");
			Db.ar(Pay.class).update(data,"^errCode|errMsg$");
		}
		
		//更新状态
		return data;
	}
	
	
	
	public Refund refund(CreateRefund param) throws PayException, IOException  {
		//查询出原来的
		Pay pay = Db.ar(Pay.class).get(param.getPayId());
		if(pay==null) {
			//订单不存在
			throw new PayException(PayException.PAY_NOT_FOUND	, "未发现原支付订单");
		}
		
		Refund refund = new Refund();
		Db.ar(Refund.class).insert(refund);
		getPayAdapter(pay.getMchId(),pay.getChannel()).refund(refund);
		//更新状态
		return refund;
	}
	
	
	
	
	
}
